package com.nightpiter.controller.log;

import java.util.Stack;

/**
 * Simple suitable Logger class
 * 
 * @author Ilya Shurigyn (honorik@gmail.com)
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class Logger {

	/**
	 * logging on/off switcher
	 */
	private static boolean logsEnabled = true;

	/** this field must be initialized before launch */
	private static AbstractLogger logger;

	public static final String TAG = "NightPiter";

	public static final String WARNING = "WARNING";
	public static final String NOTICE = " NOTICE";
	public static final String ERROR = "  ERROR";
	public static final String DEFAULT = NOTICE;
	public static final String EXCEPTION = "EXCEPTION";
	private static final String TIMER = "  TIMER";

	/**
	 * stack structure to organize time profiling
	 */
	private static Stack<Long> profilerTimeStack = new Stack<Long>();

	/**
	 * pseudo-tree organization
	 */
	private static String treePrefix = "";

	private static final String TREE_ELEMENT = " |";

	/**
	 * write to log with ERROR tag
	 * 
	 * @param message
	 *            the message to log
	 * @return the log record time in millis
	 */
	public static long writeError(String message) {
		return log(ERROR, message);
	}

	/**
	 * write to log with WARNING tag
	 * 
	 * @param message
	 *            the message to log
	 * @return the log record time in millis
	 */
	public static long writeWarning(String message) {
		return log(WARNING, message);
	}

	/**
	 * write to log with NOTICE tag
	 * 
	 * @param message
	 *            the message to log
	 * @return the log record time in millis
	 */
	public static long writeNotice(String message) {
		return log(NOTICE, message);
	}

	/**
	 * write an exception to log with EXCEPTION tag
	 * 
	 * @param e
	 *            the exception, a message of which will be logged
	 * @return
	 */
	public static long logException(Throwable e) {
		return log(EXCEPTION, "(" + e.getClass().getSimpleName() + ") "
				+ e.getMessage());
	}

	/**
	 * write an exception to log with custom tag
	 * 
	 * @param e
	 *            the exception, a message of which will be logged
	 * @return
	 */
	public static void logException(String tag, Throwable e) {
		log(tag, "(" + e.getClass().getSimpleName() + ") " + e.getMessage());
	}

	/**
	 * logging message saving logging time;
	 * 
	 * @param message
	 *            - message to log
	 * @return log record time
	 */
	public static long log(String message) {
		return log(NOTICE, message, false);
	}

	/**
	 * logging message saving logging time;
	 * 
	 * @param tag
	 *            - message tag
	 * @param message
	 *            - message to log
	 * @return log record time
	 */
	public static long log(String tag, String message) {
		return log(tag, message, false);
	}

	/**
	 * logging message saving logging time;
	 * 
	 * @param object
	 *            - the caller
	 * @param message
	 *            - message to log
	 * @return log record time
	 */
	public static long log(Object object, String message) {
		if (object != null) {
			return log(object.getClass().getSimpleName(), message, false);
		} else {
			return log(NOTICE, message, false);
		}
	}

	/**
	 * logging message saving logging time;
	 * 
	 * @param tag
	 *            - message tag
	 * @param message
	 *            - message to log
	 * @return log record time
	 */
	private static long log(String tag, String message, boolean isProfilerTimer) {
		if (!logsEnabled) {
			return 0;
		}

		long millis = 0;

		String prefix = treePrefix;
		if (isProfilerTimer) {
			prefix += " ";
		} else {
			prefix += "-";
			millis = System.currentTimeMillis();
		}

		logger.log(TAG, prefix + message);

		// toDo
		return millis;
	}

	/**
	 * save current time for profiling
	 * 
	 * @param message
	 */
	public static void logProfilerBegin(String message) {
		if (!logsEnabled) {
			return;
		}

		profilerTimeStack.push(log(message));
		treePrefix += TREE_ELEMENT;
	}

	/**
	 * retrieves last saved time and logs the difference
	 * 
	 * @param message
	 */
	public static void logProfilerEnd(String message) {
		if (!logsEnabled) {
			return;
		}

		if (profilerTimeStack.isEmpty()) {
			return;
		}

		long time = profilerTimeStack.pop();
		long timeInterval = Math.abs(System.currentTimeMillis() - time);
		int depth = profilerTimeStack.size();
		treePrefix = "";
		for (int i = 0; i < depth; i++) {
			treePrefix += TREE_ELEMENT;
		}
		log(NOTICE, timeInterval + " millis (" + message + ")", true);
		log(NOTICE, "", true);
	}

	/**
	 * Calculate time between two log records and write it to log
	 * 
	 * @param start
	 * @param finish
	 * @return time between two records
	 */
	public static long writeTimeInterval(long startRec, long finishRec) {
		long timeInterval = Math.abs(finishRec - startRec);
		log(TIMER, "    Processing time: " + ((float) timeInterval) / 1000
				+ " seconds");
		return timeInterval;
	}

	/**
	 * Calculate time elapsed after the record added and write it to log
	 * 
	 * @param start
	 * @return time has elapsed after the record added
	 */
	public static long writeTimeInterval(long startRec) {
		long timeInterval = Math.abs(System.currentTimeMillis() - startRec);
		log(TIMER, "    Processing time: " + ((float) timeInterval) / 1000
				+ " seconds");
		return timeInterval;
	}

	public static void initLogger(AbstractLogger logger) {
		Logger.logger = logger;
	}

	/**
	 * @param context
	 *            the context to set
	 */
	public static void openLog(AbstractLogger logger) {
		Logger.logger = logger;
		log("");
		log("");
		log("-------", "------- Application start -------");
	}

}
